#include "globals.h"
#include "errno.h"

#include "util/debug.h"

#include "proc/kthread.h"
#include "proc/kmutex.h"

/*
 * IMPORTANT: Mutexes can _NEVER_ be locked or unlocked from an
 * interrupt context. Mutexes are _ONLY_ lock or unlocked from a
 * thread context.
 */

void
kmutex_init(kmutex_t *mtx)
{
	sched_queue_init( &(mtx->km_waitq) );
	mtx->km_holder = NULL;
   /*     NOT_YET_IMPLEMENTED("PROCS: kmutex_init");*/
}

/*
 * This should block the current thread (by sleeping on the mutex's
 * wait queue) if the mutex is already taken.
 *
 * No thread should ever try to lock a mutex it already has locked.
 */
void
kmutex_lock(kmutex_t *mtx)
{
	KASSERT(curthr && (curthr != mtx->km_holder));
	dbg(DBG_CORE,"(GRADING 5.a)In kmutex_lock(): The current thread with pid %d is not mutex holder\n",curthr->kt_proc->p_pid);

	if(mtx->km_holder != NULL){
		sched_sleep_on( &(mtx->km_waitq) );
		mtx->km_holder = curthr;
	}

	mtx->km_holder = curthr;
	dbg(DBG_CORE,"In kmutex_lock(): The current thread with pid %d is now mutex holder\n",curthr->kt_proc->p_pid);
/*        NOT_YET_IMPLEMENTED("PROCS: kmutex_lock");*/
}

/*
 * This should do the same as kmutex_lock, but use a cancellable sleep
 * instead.
 */
int
kmutex_lock_cancellable(kmutex_t *mtx)
{
	KASSERT(curthr && (curthr != mtx->km_holder));
	dbg(DBG_CORE,"(GRADING 5.b)In kmutex_lock_cancellable(): The current thread with proc %s(pid:%d) is not mutex holder\n",curthr->kt_proc->p_comm,curthr->kt_proc->p_pid);

	if(mtx->km_holder != NULL){
/*		int returnvalue;*/
/*		returnvalue = sched_cancellable_sleep_on( &(mtx->km_waitq) );*/
		if(sched_cancellable_sleep_on( &(mtx->km_waitq) ) == -EINTR){
			return -EINTR;
		}
	}

	mtx->km_holder = curthr;
/*        NOT_YET_IMPLEMENTED("PROCS: kmutex_lock_cancellable");*/
	dbg(DBG_CORE,"(GRADING 5.b)In kmutex_lock_cancellable(): The current thread with proc %s(pid:%d) is now mutex holder\n",curthr->kt_proc->p_comm,curthr->kt_proc->p_pid);
        return 0;
}

/*
 * If there are any threads waiting to take a lock on the mutex, one
 * should be woken up and given the lock.
 *
 * Note: This should _NOT_ be a blocking operation!
 *
 * Note: Don't forget to add the new owner of the mutex back to the
 * run queue.
 *
 * Note: Make sure that the thread on the head of the mutex's wait
 * queue becomes the new owner of the mutex.
 *
 * @param mtx the mutex to unlock
 */
void
kmutex_unlock(kmutex_t *mtx)
{
	KASSERT(curthr && (curthr == mtx->km_holder));
	dbg(DBG_CORE,"(GRADING 5.c)In kmutex_unlock(): The current thread with proc %s(pid:%d) is not mutex holder\n",curthr->kt_proc->p_comm,curthr->kt_proc->p_pid);

	if(mtx->km_waitq.tq_size != 0){
		kthread_t *head;
		/*把head从waitq中dequeue出来，然后make runnable*/
		head = sched_wakeup_on( &(mtx->km_waitq) );
		mtx->km_holder = head;
	}
	else
		mtx->km_holder = NULL;

	KASSERT(curthr != mtx->km_holder);
	dbg(DBG_CORE,"(GRADING 5.c)In kmutex_unlock(): The current thread with proc %s(pid:%d) is now not mutex holder\n",curthr->kt_proc->p_comm,curthr->kt_proc->p_pid);

/*        NOT_YET_IMPLEMENTED("PROCS: kmutex_unlock");*/
}
